package com.klikmee.sms.core.base.communicator.mtel;

import hr.tis.mgw.client.GatewayException;
import hr.tis.mgw.client.message.DeliveryReport;
import hr.tis.mgw.client.message.MessageId;
import hr.tis.mgw.client.message.MessageManager2;
import hr.tis.mgw.client.message.ReceivedSms;
import hr.tis.mgw.client.message.SmsMessage;
import hr.tis.mgw.client.message.Status;
import hr.tis.mgw.client.message.notification.MessageListener2;

import java.text.MessageFormat;
import java.util.Date;
import java.util.List;

import javax.print.attribute.standard.MediaSize.Other;

import org.apache.log4j.Logger;

import com.klikmee.sms.core.base.Core;
import com.klikmee.sms.core.base.dao.ProviderDAO;
import com.klikmee.sms.core.base.communicator.SendSmsIntercepter;
import com.klikmee.sms.core.base.communicator.mtel.exceptions.SendSmsException;
import com.klikmee.sms.core.base.util.Constants;
import com.klikmee.sms.core.base.util.DeliveryReports;
import com.klikmee.sms.core.commons.exceptions.ErrorInfo;
import com.klikmee.sms.core.commons.exceptions.ExceptionHandler;
import com.klikmee.sms.core.model.BulkMessage;
import com.klikmee.sms.core.model.IncomingMessage;
import com.klikmee.sms.core.model.OutgoingMessage;
import com.klikmee.sms.core.model.Provider;

/**
 * This Class represents communicator for Mtel provider
 */
public class MTelCommunicatorImpl implements MTelCommunicator {

	/**
	 * Ref to the {@link Logger} object used to log events and messages
	 */
	private Logger						logger				= Logger.getLogger( MTelCommunicatorImpl.class );
	/**
	 * Reference to the {@link Core} object responsible global message
	 * management
	 */
	private Core						core				= null;

	/**
	 * Instance of the mtel provider
	 */
	private Provider					mtelProvider		= null;

	/**
	 * Reference to the {@link MessageManager2} object responsible for
	 * communication with mGw mtel system.
	 */
	private MessageManager2				messageManager		= null;

	/**
	 * Concrete message listener used to receive message notifications
	 */
	private MessageListener2			messageListener		= null;

	/**
	 * Instance of concrete provider DAO
	 */
	private ProviderDAO					providerDAO			= null;

	/**
	 * List of intercepters of send sms message operation
	 */
	private List<SendSmsIntercepter>	sendSmsIntercepters	= null;

	/**
	 * Exception handler
	 */
	private ExceptionHandler			exceptionHandler	= null;

	/**
	 * Default constructor for {@link MTelCommunicatorImpl} class.
	 */
	public MTelCommunicatorImpl() {

	}

	/**
	 * Init method used to initialize communicator's resources and properties.
	 * Registers message listener at message manager implementation.
	 */
	public void init() {
		// register message listener for
		messageManager.registerMessageListener( MessageManager2.REG_ID_ANY, messageListener );
		logger.info( "Registered message listener" );
		// load the provider instance
		this.mtelProvider = providerDAO.findByName( Constants.PROVIDER_MTEL );

		logger.info( "Mtel communicator initialized" );
	}

	/**
	 * Sends sms message. Parameters are extracted from {@link OutgoingMessage}
	 * object. This method then calls
	 * {@link MessageManager2#sendMessage(hr.tis.mgw.client.message.Message, String[], String, hr.tis.mgw.client.message.Priority, hr.tis.mgw.client.charging.Charging)}
	 * method which encapsulates communication with Mtel provider. Mtel returns
	 * MessageId param which can be used to retreive delivery status for the
	 * message.
	 * 
	 * @param outgoingMessage
	 *            Outgoing message to send
	 */
	@Override
	public void sendMessage(OutgoingMessage outgoingMessage) {

		// prepare message parmeters
		String messageText = outgoingMessage.getMessage();
		String[] destinations = new String[] { outgoingMessage.getToNumber() };
		String orignirator = outgoingMessage.getService().getmTelSenderId();

		// prepare message object
		SmsMessage smsMessage = new SmsMessage( messageText );

		try {
			MessageId messageId = this.messageManager.sendMessage( smsMessage, destinations, orignirator, null, null );
			outgoingMessage.setProvidersMesssageId( messageId.getId() );
			logger.info( MessageFormat.format( "Message id = [{0}], text = [{1}] successfully sent, mtel message id [{2}] assigned", outgoingMessage.getId(), outgoingMessage.getMessage(), messageId.getId() ) );
			// notify core about message sent event
			this.core.onMessageSent( outgoingMessage );

		}
		catch (GatewayException e) {

			SendSmsException sendSmsException = SendSmsException.create( "Error sending message via mtel communicator", e );
			ErrorInfo errorInfo = sendSmsException.addInfo();
			errorInfo.setContextId( "SendSMS" );
			errorInfo.setErrorId( "Gateway exception" );
			errorInfo.getParameters().put( "MTel server url", messageManager.getServerURL() );
			errorInfo.getParameters().put( "Outgoing message id", outgoingMessage.getId() );

			exceptionHandler.handle( sendSmsException );
			// }

		}
		catch (Exception e) {
			exceptionHandler.handle( e );
		}

	}

	/**
	 * Invoked on new sms messages received over Mtel operater. Message
	 * attributes are extracted and passed over to Core in form of
	 * {@link IncomingMessage} object
	 * 
	 * @param receivedSms
	 *            mtel sms message representation
	 */
	@Override
	public void onSmsMessage(ReceivedSms receivedSms) {
		// this is core representation of the new message
		IncomingMessage message = new IncomingMessage();

		// set message attributes
		message.setFromNumber( receivedSms.getSenderAddress() );
		message.setMessage( receivedSms.getSmsMessage().getText() );
		message.setOurIncomingTime( new Date() );
		message.setProvider( this.mtelProvider );
		message.setToNumber( receivedSms.getDestinationAddress() );
		message.setTypeOfMessage( Constants.MESSAGE_TYPE_SMS );

		core.enqueueIncomingMessage( message );

		logger.info( String.format( "Sms message from %s forwarded to core.", receivedSms.getSenderAddress() ) );

	}

	/**
	 * Invoked on new delivery reports received over MTel communicator. Delivery
	 * report attributes are extracted and passed to Core in form of
	 * {@link OutgoingMessage} object.
	 * 
	 * @param deliveryReport
	 *            mtel delivery report representation
	 */
	@Override
	public void onDeliveryReport(DeliveryReport deliveryReport) {

		// holder for the status resport
		com.klikmee.sms.core.base.communicator.DeliveryReport coreDeliveryReport = new com.klikmee.sms.core.base.communicator.DeliveryReport();

		// resolve status
		int status = resolveStatus( deliveryReport.getStatus() );

		// get providers message id
		String messageId = deliveryReport.getMessageId();

		// setup message params
		coreDeliveryReport.setProvidersMessageId( messageId );
		coreDeliveryReport.setStatus(status );
		coreDeliveryReport.setProvider( this.mtelProvider );

		core.enqueueStatusReport( coreDeliveryReport );

		logger.info( String.format( "Received delivery report for messageId = [%s], status = [%s]", deliveryReport.getMessageId(), deliveryReport.getStatus().getValue() ) );
	}

	/**
	 * Test is bulk message can be sent via mtel provider. Takes the receiver of
	 * bulk message and test is against mtel regex.
	 * 
	 * @param receiver
	 *            receiver of the bulk message
	 * 
	 * @return true if receiver matches expression, otherwise false
	 */
	@Override
	public boolean canHandleBulkMessage(String receiver) {

		boolean result = receiver.matches( mtelProvider.getMobilePhonePrefixes() );
		return result;
	}

	@Override
	public List<SendSmsIntercepter> getSendSmsIntercepters() {
		// TODO Auto-generated method stub
		return this.sendSmsIntercepters;
	}

	/**
	 * Resolves mtel status report to core's native status report.
	 * 
	 * @param status
	 *            mtel status obj to be resolved
	 * @return native status report value
	 */
	private int resolveStatus(Status status) {
		if (status.equals( Status.DELIVERED )) {
			return DeliveryReports.DELIVERED;
		}
		else if (status.equals( Status.DELIVERY_IMPOSSIBLE )) {
			return DeliveryReports.MTel.DELIVERY_IMPOSSIBLE;
		}

		else if (status.equals( Status.DELIVERY_UNCERTAIN )) {
			return DeliveryReports.MTel.DELIVERY_UNCERTAIN;
		}

		else if (status.equals( Status.MESSAGE_WAITING )) {
			return DeliveryReports.MTel.MESSAGE_WAITING;
		}

		else
			return -1;
	}

	@Override
	public void sendBulkMessage(BulkMessage bulkMessage) {
		// prepare message parmeters
		String messageText = bulkMessage.getText();
		String[] destinations = new String[] { bulkMessage.getToNumber() };
		String orignirator = bulkMessage.getChargingCategory().getService().getmTelSenderId();

		// prepare bulk message
		SmsMessage smsMessage = new SmsMessage( messageText );
		try {
			MessageId messageId = this.messageManager.sendMessage( smsMessage, destinations, orignirator, null, null );
			bulkMessage.setPrividersId( messageId.getId() );
			logger.info( MessageFormat.format( "Bulk message id = [{0}], text = [{1}] successfully sent", bulkMessage.getId(), bulkMessage.getText() ) );
			// notify core about message sent event
			this.core.onBulkMessageSent( bulkMessage );

		}
		catch (GatewayException e) {

			SendSmsException sendSmsException = SendSmsException.create( "Error sending bulk message via mtel communicator", e );
			ErrorInfo errorInfo = sendSmsException.addInfo();
			errorInfo.setContextId( "SendBulk" );
			errorInfo.setErrorId( "Gateway exception" );
			errorInfo.getParameters().put( "MTel server url", messageManager.getServerURL() );
			errorInfo.getParameters().put( "Bulk message id", bulkMessage.getId() );

			exceptionHandler.handle( sendSmsException );
			// }

		}
		catch (Exception e) {
			exceptionHandler.handle( e );
		}
	}

	public void setMessageManager(MessageManager2 messageManager) {
		this.messageManager = messageManager;
	}

	public void setCore(Core core) {
		this.core = core;
	}

	public void setMessageListener(MessageListener2 messageListener) {
		this.messageListener = messageListener;
	}

	public void setProviderDAO(ProviderDAO providerDAO) {
		this.providerDAO = providerDAO;
	}

	public void setSendSmsIntercepters(List<SendSmsIntercepter> sendSmsIntercepters) {
		this.sendSmsIntercepters = sendSmsIntercepters;
	}

	public void setExceptionHandler(ExceptionHandler exceptionHandler) {
		this.exceptionHandler = exceptionHandler;
	}

	@Override
	public Provider getProvider() {
		return this.mtelProvider;
	}

}
